home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 November: Tool Chest / Dev.CD Nov 94.toast / Sample Code / Snippets / Processes / LaunchWithDoc2.1 / LaunchWithDoc2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-07-18  |  15.5 KB  |  513 lines  |  [TEXT/MPS ]

  1. /*
  2.  *   LaunchWithDoc v 2.1
  3.  *
  4.  *   Greg Robbins  August 1993, 
  5.  *   modified by Nitin Ganatra, July 1994
  6.  *
  7.  *   Document-launching sample program
  8.  *   Loosely based on C.K. Haun's LaunchWithDoc
  9.  *
  10.  *   This snippet includes these useful routines:
  11.  *
  12.  *      OpenSpecifiedDocument
  13.  *        finds the creator application for a document, whether or not
  14.  *        the app is running, launches the app if necessary, and sends
  15.  *        the Apple Event necessary to get the app to open the document
  16.  *
  17.  *      FindApplicationFromDocument
  18.  *        searches the mounted volumes for the application which
  19.  *        created a document
  20.  *
  21.  *      LaunchApplicationWithDocument
  22.  *        launches an application which is not running and passes it
  23.  *        the OpenDocuments event as part of the launch parameters
  24.  *
  25.  *      SendOpenDocumentEventToProcess
  26.  *        sends an OpenDocuments Apple event to a running program
  27.  *
  28.  *      BuildOpenDocumentsEvent
  29.  *        utility function to build an 'odoc' event from a list of 
  30.  *        FSSpecs.
  31.  *
  32.  *  Remember that a target application need not be Apple event aware
  33.  *  in order for the OpenDocuments event to succeed (the System will
  34.  *  pull "puppet strings", simulating the events necessary to make the
  35.  *  target app open the document)
  36.  *
  37.  *  However, LaunchWithDoc must be high level event aware (as set in the
  38.  *  SIZE resource) in order to send Apple events using AESend
  39.  *
  40.  */
  41.  
  42. #include <QuickDraw.h>
  43. #include <StandardFile.h>
  44. #include <Fonts.h>
  45. #include <Menus.h>
  46. #include <Dialogs.h>
  47. #include <Events.h>
  48. #include <Files.h>
  49. #include <TextEdit.h>
  50. #include <Memory.h>
  51. #include <Errors.h>
  52. #include <Processes.h>
  53. #include <AppleEvents.h>
  54. #include <Aliases.h>
  55.  
  56. // prototypes
  57.  
  58. OSErr OpenSpecifiedDocument(const FSSpec * documentFSSpecPtr);
  59. OSErr FindApplicationFromDocument(const FSSpec * documentFSSpecPtr,
  60.     FSSpecPtr applicationFSSpecPtr);
  61. void ReportError(StringPtr messageStr);
  62.  
  63. // what functions changed since LaunchWithDoc2
  64.  
  65. OSErr BuildOpenDocumentEvent(ProcessSerialNumber *targetPSN, 
  66.             const FSSpec *theSpecArray, const short numOfSpecs, AppleEvent *odocAppleEvent);
  67. OSErr SendOpenDocumentEventToProcess(ProcessSerialNumber *targetPSN,
  68.             const FSSpec *theSpecArray, const short numOfSpecs);
  69. OSErr LaunchApplicationWithDocument(const FSSpec *applicationFSSpecPtr,
  70.             const FSSpec *theSpecArray, const short numOfSpecs);
  71.  
  72.  
  73. // main program
  74. //
  75. // the main routine raises a std file dialog to let the
  76. // user choose a document and then opens the document
  77. // in the appropriate application
  78.  
  79. void main(void)
  80. {
  81.     OSErr retCode;
  82.     StandardFileReply documentStdFileReply;
  83.     SFTypeList mySFTypeList;
  84.     short index;
  85.     FSSpec    appSpec, docTmpSpec[3];
  86.     
  87.     // initialize the toolbox
  88.     InitGraf(&qd.thePort); InitFonts(); InitWindows(); InitMenus();
  89.     TEInit(); InitDialogs(nil); InitCursor();
  90.     
  91.     // Simplest case: get a document and open it
  92.     StandardGetFile(nil, -1, mySFTypeList, &documentStdFileReply);
  93.     if (documentStdFileReply.sfGood) {
  94.         
  95.         retCode = OpenSpecifiedDocument(&documentStdFileReply.sfFile);
  96.         if (retCode != noErr) ReportError("\p OpenDocument failed");
  97.     }
  98.  
  99. /*    
  100.     // Another case: get an application to launch and three documents
  101.     // to open on startup
  102.     StandardGetFile(nil, -1, mySFTypeList, &documentStdFileReply);
  103.     if (documentStdFileReply.sfGood) {
  104.  
  105.         appSpec = documentStdFileReply.sfFile;
  106.         for (index = 0; index < 3; index ++) {
  107.             StandardGetFile(nil, -1, mySFTypeList, &documentStdFileReply);
  108.             if (documentStdFileReply.sfGood)
  109.                 docTmpSpec[index] = documentStdFileReply.sfFile;
  110.  
  111.         }
  112.         
  113.         retCode = LaunchApplicationWithDocument(&appSpec, docTmpSpec, index);
  114.     }
  115. */
  116.  
  117. }
  118.  
  119. void ReportError(StringPtr messageStr)
  120. {
  121.     DebugStr(messageStr);
  122. }
  123.  
  124.  
  125. // OpenSpecifiedDocument searches to see if the application which
  126. // created the document is already running.  If so, it sends
  127. // an OpenSpecifiedDocuments Apple event to the target application
  128. // (remember that, because of puppet strings, this works even
  129. // if the target application is not Apple event-aware.)
  130.  
  131. OSErr OpenSpecifiedDocument(const FSSpec * documentFSSpecPtr)
  132. {
  133.     OSErr retCode;
  134.     ProcessSerialNumber currPSN;
  135.     ProcessInfoRec currProcessInfo;
  136.     FSSpec applicationSpec;
  137.     FInfo documentFInfo;
  138.     Boolean foundRunningProcessFlag;
  139.     
  140.     // verify the document file exists and get its creator type
  141.     
  142.     retCode = FSpGetFInfo(documentFSSpecPtr, &documentFInfo);
  143.     if (retCode != noErr) goto Bail;
  144.     
  145.     // check the current processes to see if the creator app is already
  146.     // running, and get its process serial number (as currPSN)
  147.     
  148.     currPSN.lowLongOfPSN = kNoProcess;
  149.     currPSN.highLongOfPSN = 0;
  150.     
  151.     currProcessInfo.processInfoLength = sizeof(ProcessInfoRec);
  152.     currProcessInfo.processName = nil;
  153.     currProcessInfo.processAppSpec = &applicationSpec;
  154.     
  155.     foundRunningProcessFlag = false;
  156.     while (GetNextProcess(&currPSN) == noErr) {
  157.         if (GetProcessInformation(&currPSN, &currProcessInfo) == noErr) {
  158.             if (currProcessInfo.processSignature == documentFInfo.fdCreator) {
  159.                 foundRunningProcessFlag = true;
  160.                 break;
  161.             }
  162.         }
  163.     }
  164.     
  165.     // if the creator is running, send it an OpenDocuments Apple event
  166.     // since there is no need to launch it
  167.     
  168.     if (foundRunningProcessFlag)
  169.         retCode = SendOpenDocumentEventToProcess(&currPSN, documentFSSpecPtr, 1);
  170.     
  171.     // else if the creator is not running, find it on disk and launch
  172.     // it with the OpenDocuments event included as a part of the
  173.     // launch parameters
  174.     
  175.     else {
  176.         retCode = FindApplicationFromDocument(documentFSSpecPtr, &applicationSpec);
  177.         
  178.         if (retCode == noErr)
  179.         
  180.             retCode = LaunchApplicationWithDocument(&applicationSpec,
  181.                 documentFSSpecPtr, 1);
  182.     }
  183.     
  184. Bail:
  185.     return retCode;
  186. }
  187.  
  188.  
  189. //----------------------------------------------------------------------------
  190. // LaunchApplicationWithDocument
  191. //
  192. // given an application and any number of documents, 
  193. // LaunchApplicationWithDocument launches the application and passes the 
  194. // application an OpenDocuments event for the document(s)
  195. //----------------------------------------------------------------------------
  196. OSErr LaunchApplicationWithDocument(
  197.     const FSSpec        *applicationFSSpecPtr,
  198.     const FSSpec         *theSpecArray,
  199.     const short            numOfSpecs)
  200. {
  201.     OSErr retCode;
  202.     LaunchParamBlockRec launchParams;
  203.     AppleEvent theAppleEvent;
  204.     AEDesc launchParamDesc;
  205.     ProcessSerialNumber targetPSN;
  206.     
  207.     // to simplify cleanup, ensure that handles are nil to start
  208.     launchParams.launchAppParameters    = nil;
  209.     theAppleEvent.dataHandle            = nil;
  210.     launchParamDesc.dataHandle            = nil;
  211.     
  212.     if (theSpecArray != nil) {
  213.  
  214.         // because 'odoc' events require a address descriptor, I just 
  215.         // grab the PSN for the current process.  It doesn't matter what
  216.         // it is, because it's never used.
  217.         (void) GetCurrentProcess(&targetPSN);
  218.         
  219.         // build an 'odoc' event given the array of FSSpecs and the ProcessSerialNumber
  220.         retCode = BuildOpenDocumentEvent(&targetPSN, theSpecArray, numOfSpecs, &theAppleEvent);
  221.         
  222.         if (retCode == noErr) {
  223.         
  224.             // coerce the AppleEvent into app parameters, for _LaunchApplication
  225.             retCode = AECoerceDesc(&theAppleEvent, typeAppParameters, &launchParamDesc);
  226.             if (retCode != noErr) goto Bail;
  227.             
  228.             // fill in the launch parameter block, including the
  229.             // Apple event, and make the launch call
  230.             HLock((Handle) launchParamDesc.dataHandle);
  231.             launchParams.launchAppParameters =
  232.                 (AppParametersPtr) *(launchParamDesc.dataHandle);
  233.  
  234.         }
  235.  
  236.     }
  237.  
  238.     launchParams.launchBlockID        = extendedBlock;
  239.     launchParams.launchEPBLength    = extendedBlockLen;
  240.     launchParams.launchFileFlags    = launchNoFileFlags;
  241.     launchParams.launchControlFlags    = launchContinue;
  242.     launchParams.launchAppSpec        = (FSSpecPtr)applicationFSSpecPtr;
  243.  
  244.     retCode = LaunchApplication(&launchParams);
  245.  
  246. Bail:
  247.     // dispose of everything that was allocated
  248.     if (theAppleEvent.dataHandle != nil)     (void) AEDisposeDesc(&theAppleEvent);
  249.     if (launchParamDesc.dataHandle != nil)   (void) AEDisposeDesc(&launchParamDesc);
  250.     
  251.     return retCode;
  252.  
  253. }
  254.  
  255.  
  256. //----------------------------------------------------------------------------
  257. // SendOpenDocumentEventToProcess
  258. //
  259. // given an application's serial number and any number of documents, 
  260. // SendOpenDocumentEventToProcess passes 
  261. // the application an OpenDocuments event for the document
  262. //----------------------------------------------------------------------------
  263. OSErr SendOpenDocumentEventToProcess(
  264.     ProcessSerialNumber            *targetPSN,
  265.     const FSSpec                 *theSpecArray,
  266.     const short                    numOfSpecs)
  267. {
  268.     OSErr retCode;
  269.     AppleEvent theAppleEvent, theReplyEvent;
  270.  
  271.     theAppleEvent.dataHandle = nil;
  272.     retCode = BuildOpenDocumentEvent(targetPSN, theSpecArray, numOfSpecs, &theAppleEvent);
  273.  
  274.     if (retCode == noErr)
  275.         retCode = AESend(&theAppleEvent,
  276.                         &theReplyEvent, 
  277.                         kAENoReply, 
  278.                         kAENormalPriority,
  279.                         kAEDefaultTimeout,
  280.                         nil,
  281.                         nil);
  282.     
  283.     // dispose of the AppleEvent if it was allocated    
  284.     if (theAppleEvent.dataHandle != nil)  
  285.         (void) AEDisposeDesc(&theAppleEvent);
  286.     
  287.     return retCode;
  288.  
  289. }
  290.  
  291.  
  292. // FindApplicationFromDocument uses the Desktop Database to
  293. // locate the creator application for the given document
  294. //
  295. // this routine will first check the desktop database of the disk
  296. // containing the document, then the desktop database of all local
  297. // disks, then the desktop databases of all server volumes
  298. // (so up to three passes will be made)
  299.  
  300. OSErr FindApplicationFromDocument(const FSSpec * documentFSSpecPtr,
  301.     FSSpecPtr applicationFSSpecPtr)
  302. {
  303.     enum { documentPass, localPass, remotePass, donePass } volumePass;
  304.     DTPBRec desktopParams;
  305.     HParamBlockRec hfsParams;
  306.     FInfo documentFInfo;
  307.     short volumeIndex;
  308.     Boolean foundFlag;
  309.     GetVolParmsInfoBuffer volumeInfoBuffer;
  310.     OSErr retCode;
  311.     
  312.     // verify the document file exists and get its creator type
  313.     
  314.     retCode = FSpGetFInfo(documentFSSpecPtr, &documentFInfo);
  315.     if (retCode != noErr) goto Bail;
  316.     
  317.     volumePass = documentPass;
  318.     volumeIndex = 0;
  319.     
  320.     do {
  321.         
  322.         // first, find the vRefNum of the volume whose Desktop Database
  323.         // we're checking this time
  324.         
  325.         // if we're on the initial pass (documentPass) just use
  326.         // the vRefNum of the document itself
  327.         
  328.         if (volumePass == documentPass)
  329.         
  330.             desktopParams.ioVRefNum = documentFSSpecPtr->vRefNum;
  331.         
  332.         // otherwise, find the vRefNum of the next volume appropriate
  333.         // for this pass
  334.         
  335.         else {
  336.             
  337.             volumeIndex++;
  338.             
  339.             // convert the volumeIndex into a vRefNum
  340.             
  341.             hfsParams.volumeParam.ioNamePtr = nil;
  342.             hfsParams.volumeParam.ioVRefNum = 0;
  343.             hfsParams.volumeParam.ioVolIndex = volumeIndex;
  344.             retCode = PBHGetVInfoSync(&hfsParams);
  345.             
  346.             // a nsvErr indicates that the current pass is over
  347.             if (retCode == nsvErr) goto SkipThisVolume;
  348.             if (retCode != noErr) goto Bail;
  349.             
  350.             // since we handled the document volume during the documentPass,
  351.             // skip it if we have hit that volume again
  352.             
  353.             if (hfsParams.volumeParam.ioVRefNum == documentFSSpecPtr->vRefNum)
  354.                 goto SkipThisVolume;
  355.             
  356.             // call GetVolParms to determine if this volume is a server
  357.             // (a remote volume)
  358.             
  359.             hfsParams.ioParam.ioBuffer = (Ptr) &volumeInfoBuffer;
  360.             hfsParams.ioParam.ioReqCount = sizeof(GetVolParmsInfoBuffer);
  361.             retCode = PBHGetVolParmsSync(&hfsParams);
  362.             if (retCode != noErr) goto Bail;
  363.             
  364.             // if the vMServerAdr field of the volume information buffer
  365.             // is zero, this is a local volume; skip this volume
  366.             // if it's local on a remote pass or remote on a local pass
  367.             
  368.             if ((volumeInfoBuffer.vMServerAdr != 0) !=
  369.                 (volumePass == remotePass)) goto SkipThisVolume;
  370.             
  371.             // okay, now we've found the vRefNum for our desktop database call
  372.             
  373.             desktopParams.ioVRefNum = hfsParams.volumeParam.ioVRefNum;
  374.         }
  375.         
  376.         // find the path refNum for the desktop database for
  377.         // the volume we're interested in
  378.         
  379.         desktopParams.ioNamePtr = nil;
  380.         
  381.         retCode = PBDTGetPath(&desktopParams);
  382.         if (retCode == noErr && desktopParams.ioDTRefNum != 0) {
  383.         
  384.             // use the GetAPPL call to find the preferred application
  385.             // for opening any document with this one's creator
  386.             
  387.             desktopParams.ioIndex = 0;
  388.             desktopParams.ioFileCreator = documentFInfo.fdCreator;
  389.             desktopParams.ioNamePtr = applicationFSSpecPtr->name;
  390.             retCode = PBDTGetAPPLSync(&desktopParams);
  391.             
  392.             if (retCode == noErr) {
  393.             
  394.                 // okay, found it; fill in the application file spec
  395.                 // and set the flag indicating we're done
  396.                 
  397.                 applicationFSSpecPtr->parID = desktopParams.ioAPPLParID;
  398.                 applicationFSSpecPtr->vRefNum = desktopParams.ioVRefNum;
  399.                 foundFlag = true;
  400.                 
  401.             }
  402.         }
  403.         
  404.     SkipThisVolume:
  405.     
  406.         // if retCode indicates a no such volume error or if this
  407.         // was the first pass, it's time to move on to the next pass
  408.         
  409.         if (retCode == nsvErr || volumePass == documentPass) {
  410.             volumePass++;
  411.             volumeIndex = 0;
  412.         }
  413.         
  414.     } while (foundFlag == false && volumePass != donePass);
  415.     
  416. Bail:
  417.     return retCode;
  418. }
  419.  
  420.  
  421. //----------------------------------------------------------------------------
  422. // BuildOpenDocumentsEvent
  423. //
  424. // General utility to turn a ProcessSerialNumber and a list of FSSpecs into
  425. // an 'odoc' AppleEvent with the ProcessSerialNumber as the target
  426. // application.  Used by SendOpenDocumentEventToProcess, and
  427. // LaunchApplicationWithDocument.
  428. //----------------------------------------------------------------------------
  429. OSErr BuildOpenDocumentEvent(
  430.     ProcessSerialNumber        *targetPSN, 
  431.     const FSSpec             *theSpecArray, 
  432.     const short                numOfSpecs,
  433.     AppleEvent                *odocAppleEvent)
  434. {
  435.     OSErr            retCode;
  436.     AppleEvent        theAppleEvent;
  437.     AEDesc            targetAddrDesc, docDesc;
  438.     AEDescList        docDescList;
  439.     AliasHandle        docAlias;
  440.     short            counter;
  441.     FSSpecPtr        specIterator;
  442.  
  443.     // to simplify cleanup, ensure that handles are nil to start
  444.     targetAddrDesc.dataHandle    = nil;
  445.     theAppleEvent.dataHandle    = nil;
  446.     docDescList.dataHandle        = nil;
  447.     docDesc.dataHandle            = nil;
  448.     docAlias                    = nil;
  449.  
  450.     // create an address descriptor based on the serial number of
  451.     // the target process
  452.     retCode = AECreateDesc(typeProcessSerialNumber, (Ptr) targetPSN,
  453.         sizeof(ProcessSerialNumber), &targetAddrDesc);
  454.     if (retCode != noErr) goto Bail;
  455.     
  456.     // make a descriptor list containing just a descriptor with an
  457.     // alias to the document
  458.     retCode = AECreateList(nil, 0, false, &docDescList);
  459.     if (retCode != noErr) goto Bail;
  460.  
  461.     // start at the beginning of the FSSpec list, and start adding
  462.     // them to the document list descriptor
  463.  
  464.     // NOTE: we need to make sure we dispose of the aliases and the
  465.     // AE descriptor in this loop, otherwise there will be memory
  466.     // leaks.
  467.     specIterator = (FSSpecPtr)theSpecArray;
  468.     for (counter = 0; counter < numOfSpecs; counter ++) {    
  469.  
  470.         retCode = NewAlias(nil, &specIterator[counter], &docAlias);
  471.         if (retCode != noErr) goto Bail;
  472.         
  473.         HLock((Handle) docAlias);
  474.         retCode = AECreateDesc(typeAlias, (Ptr) *docAlias, 
  475.             InlineGetHandleSize((Handle) docAlias), &docDesc);
  476.         HUnlock((Handle) docAlias);
  477.         
  478.         if (retCode != noErr) goto Bail;
  479.         // the alias is now in the AEDescriptor, so dispose of it
  480.         DisposeHandle((Handle)docAlias); docAlias = nil;
  481.         
  482.         retCode = AEPutDesc(&docDescList, 0, &docDesc);
  483.         if (retCode != noErr) goto Bail;
  484.  
  485.         // the alias is now in the AE document list, so dispose of it
  486.         retCode = AEDisposeDesc(&docDesc);
  487.         if (retCode != noErr) goto Bail;
  488.  
  489.     }
  490.     
  491.     // now make the 'odoc' AppleEvent descriptor and insert the 
  492.     // document descriptor list as the direct object
  493.     retCode = AECreateAppleEvent(kCoreEventClass, kAEOpenDocuments,
  494.         &targetAddrDesc, kAutoGenerateReturnID, kAnyTransactionID,
  495.         &theAppleEvent);
  496.     if (retCode != noErr) goto Bail;
  497.     
  498.     retCode = AEPutParamDesc(&theAppleEvent, keyDirectObject, &docDescList);
  499.     if (retCode != noErr) goto Bail;
  500.     
  501.     *odocAppleEvent = theAppleEvent;
  502.  
  503. Bail:
  504.     // dispose of everything that was allocated, except the return AE
  505.     if (targetAddrDesc.dataHandle != nil)  (void) AEDisposeDesc(&targetAddrDesc);
  506.     if (docDescList.dataHandle != nil)  (void) AEDisposeDesc(&docDescList);
  507.     if (docDesc.dataHandle != nil)  (void) AEDisposeDesc(&docDesc);
  508.     if (docAlias != nil)  DisposeHandle((Handle) docAlias);
  509.     
  510.     return retCode;
  511.  
  512. }
  513.